Table Of Contents

Previous topic

Interaction class

Next topic

CoreMirror class

This Page

FastNeighborList class

This class extends the ASE NeighborList class to provide a more efficient neighbor finding tool. The neighbor finding routine searches the neighborhoods of all atoms and for each atom records which other atoms are closer than a given cutoff distance.

The benefit of neighbor lists

Atomistic pair and many-body potentials typically depend on the local atomic structure and especially the relative coordinates of the atoms. However, finding the separation vector and distance between coordinates in periodic 3D space is computationally fairly costly operation and the number of atom-atom pairs in the system grows as \(\mathcal{O}(n^2)\). Therefore the evaluation of local potentials can be made efficient by storing lists of nearby atoms for all particles to narrow down the scope of search for interacting neighbors.

Typically one chooses a cutoff distance \(r_\mathrm{cut}\) beyond which the atoms do not see each other. Then, the neighbor lists should always contain all the atoms within this cutoff radius \(r_{ij} \le r_\mathrm{cut}\). In dynamic simulations where the atoms move, the typical scheme is to list atoms within a slightly longer radius, \(r_\mathrm{cut} + r_\mathrm{skin}\) because then the lists need not be updated until an atom has moved by more than \(r_\mathrm{skin}\).

The skin width is a static variable of FastNeighborList, which you can directly change with:

new_skin_width = 1.0
pysic.calculator.FastNeighborList.neighbor_marginal = new_skin_width

The currently set default value is automatically used when Pysic needs to build the neighbor list.

Limitations in the implementation

Since the fast algorithm is implemented in Fortran, it operates on the structure allocated in the Fortran core. Therefore, even though the build() method takes an ASE Atoms object as an argument, it does not analyze the given structure. It does check against CoreMirror to see if the given structure matches the one in the core and raises an error if not, but accessing the core has to still be done through Pysic. When Pysic is run normally, this is automatically taken care of. As the implementation is MPI parallelized, it is also necessary that the MPI environment has been set up - especially the distribution of load (i.e. atoms) between processors must be done before the lists can be built.

Another more profound limitation in the current implementation of the algorithm is the fact that it limits the neighbor finding to neighboring subvolumes. Since the subvolumes are not allowed to be larger than the actual simulation volume, the cutoffs cannot be longer than the shortest perpendicular separation between facets of the subvolume. For rectangular cells, this is just the minimum of the lengths of the vectors spanning the cell, \(\mathbf{v}_{i,j,k}\). For inclined cell shapes, the perpendicular distance between cell facets, \(d\), is

\[\begin{eqnarray} d_i & = & \frac{|\mathbf{v}_i \cdot \mathbf{n_i}|}{|\mathbf{n}_{i}|}\\ \mathbf{n}_i & = & \mathbf{v}_j \times \mathbf{v}_k \end{eqnarray}\]

where \(\mathbf{n}_i\) are the normal vectors of the plane spanned by the vectors \(\mathbf{v}_{j,k}\). If one wishes to find neighbors in a radius containing the simulation volume several times, the original ASE NeighborList should be used instead. Pysic does this choice automatically when building the neighbor lists. One should usually avoid such long cutoffs in the first place, but if your system is very small that may not be possible.

Accessing neighbor lists

Although the neighbor lists are automatically used by the calculator, it is also possible to use the list for manually analysing local atomic neighborhoods. This can be done with the methods

which list, for a specified atom, the indices and periodic boundary offsets of neighbors (cf. get_neighbors of the ASE neighbor list), separation vectors, and separation distances, respectively. The arrays can also be requested pre-sorted according to the atom-atom distance by providing the keyword sort=True.

Since the lists are created in the Fortran core according to specified interaction cutoffs, it is unfortunately not possible to inquire the neighbors within arbitrary radii without touching the potentials. Normally the lists contain the atoms which interact. In order to just analyse structures, a dummy calculator needs to be created:

system = ase.Atoms(...)

# create a dummy calculator
dummy = pysic.Pysic()
# Find neighbors at distance 3.0 + 0.5 (0.5 is the default marginal).
# Note that the list finds all neighbors within the maximum interaction
# radius of the particular atom.
pot = pysic.Potential('LJ', cutoff=3.0, symbols=...)
dummy.add_potential(pot)
system.set_calculator(dummy)
# It is important to manually initialize the core since no actual calculations are carried out.
dummy.set_core()

# get the list and access its contents
nbl = dummy.get_neighbor_list()
neighbors, offsets = nbl.get_neighbors(0, system, True)
separations = nbl.get_neighbor_separations(0, system, True)
distances = nbl.get_neighbor_distances(0, system, True)

Methods inherited from ASE NeighborList

Full documentation of the FastNeighborList class

class pysic.calculator.FastNeighborList(cutoffs, skin=None)[source]

ASE has a neighbor list class built in, ASE NeighborList, but its implementation is currently inefficient, and building of the list is an \(O(n^2)\) operation. This neighbor list class overrides the build() method with an \(O(n)\) time routine. The fast routine is based on a spatial partitioning algorithm.

The way cutoffs are handled is also somewhat different to the original ASE list. In ASE, the distances for two atoms are compared against the sum of the individual cutoffs + neighbor list skin. This list, however, searches for the neighbors of each atom at a distance of the cutoff of the given atom only, plus skin.

build(atoms)[source]

Builds the neighbor list.

The routine requires that the given atomic structure matches the one in the core. This is because the method invokes the Fortran core to do the neighbor search. The method overrides the similar method in the original ASE neighborlist class, which directly operates on the given structure, so this method also takes the atomic structure as an argument. However, in order to keep the core modification routines in the Pysic class, this method does not change the core structure. It does raise an error if the structures do not match, though.

The neighbor search is done via the generate_neighbor_lists() routine. The routine builds the neighbor list in the core, after which the list is fed back to the FastNeighborList object by looping over all atoms and saving the lists of neighbors and offsets.

Parameters:

atoms: ASE Atoms object
the structure for which the neighbors are searched
get_neighbor_distances(index, atoms, sort=False)

Returns a list of atom-atom distances between the given atom and its neighbors.

Parameters:

index: integer
the index of the central atom
atoms: ASE Atoms
the atoms object containing the absolute coordinates
sort: boolean
if True, the list will be sorted according to distance
get_neighbor_separations(index, atoms, sort=False)

Returns an array of atom-atom separation vectors between the given atom and its neighbors.

Parameters:

index: integer
the index of the central atom
atoms: ASE Atoms
the atoms object containing the absolute coordinates
sort: boolean
if True, the list will be sorted according to distance
get_neighbors(index, atoms=None, sort=False)

Returns arrays containing the indices and offsets of the neighbors of the given atom.

Overrides the method in ASE NeighborList.

Parameters:

index: integer
the index of the central atom
atoms: ASE Atoms
the atoms object containing the absolute coordinates - needed only if sorting is necessary
sort: boolean
if True, the list will be sorted according to distance
neighbor_marginal = 0.5

Default skin width for the neighbor list